#ifndef _LOG_H_
#define _LOG_H_

#include <errno.h>
#include <stdio.h>
#include <stdbool.h>

#if(__linux__ || __unix__)
	// 使用 syslog 中的日志级别定义
	#include <syslog.h>
#else
	// 定义日志级别
	typedef enum {
		LOG_EMERG,
		LOG_ALERT,
		LOG_CRIT,
		LOG_ERR,
		LOG_WARNING,
		LOG_NOTE,
		LOG_INFO,
		LOG_DEBUG,
        LOG_RAW,   // 不带时间及日志级别等格式，只输出信息
        LOG_MAX
	} log_level_t;
#endif //HAS_SYSLOG

#include "std_err.h"
#include "unios.h"

/// 日志数量
#define LOG_MAX_LOGGER	4
/// 日志 writers 数量
#define LOG_MAX_WRITER	8
/// 日志名称长度
#define LOG_NAME_SIZE	32
/// 日志缓冲区大小
#define LOG_BUFFER_SIZE 1024

// forward 声明
typedef struct _transceiver transceiver_t;

// 基础日志 writer 数据结构
typedef struct _log_writer {
    // write 函数指针，为空时表示此 writer 无效
    void (*write)(struct _log_writer* self, const char* msg);
    // flush writer 的写缓冲区
    void (*flush)(struct _log_writer* self);
    // 调用 close 函数指针，关闭 writer
    void (*close)(struct _log_writer* self);
} log_writer_t;

// write 函数类型
typedef void (*log_writefunc_t)(log_writer_t* self, const char* msg);

typedef struct _logger {
    /**
     * 日志输出单个字符
     * @param logger 日志
     * @param ch 要输出的字符
     * @return 写入的长度
     */
    int (*putc)(struct _logger* logger, const char ch);

    /**
     * 日志输出字符串
     * @param logger 日志
     * @param msg 要输出的信息
     * @return 写入的长度
     */
    int (*puts)(struct _logger* logger, const char* msg);

    /**
     * 日志输出格式化字符串
     * @param logger 日志
     * @param format 同 printf() 格式字符串
     * @param ... 同 printf() 可变参数
     * @return 写入的长度
     */
    int (*printf)(struct _logger* logger, const char* format, ...);

    /**
     * 打印输出二进制缓冲区数据
     * @param logger 日志
     * @param buf 二进制缓冲区
     * @param len 要打印的长度
     * @param cpl 每行打印多少个字节数据
     * @param show_offset 是否显示每行对应的偏移地址
     * @param show_ascii 是否显示字节对应的 ASCII 码
     */
    void (*dumpbin)(struct _logger* logger, const void *buf, size_t len, size_t cpl, bool show_offset, bool show_ascii);

    /**
     * 日志输出函数
     * @param logger 日志
     * @param level 日志级别
     * @param tag 标签
     * @param format 格式化
     * @param ... 格式化参数
     * @return 写入的长度
     */
    int (*log)(struct _logger* logger, int level, const char* tag, const char* format, ...);

    /**
     * flush 日志写缓冲区函数
     */
    void (*flush)(struct _logger* logger);

    // 当前设置的日志级别
    int level;
    // 挂接的 writers
    log_writer_t* writers[LOG_MAX_WRITER];
    // 用于避免多线程同时输出的临界区
    unios_critical_section_t cs;
} logger_t;


#ifdef __cplusplus
extern "C" {
#endif

/**
 * 初始化已有的日志
 * @return 初始化后的日志
 */
logger_t* logger_init(logger_t *logger);

/**
 * @brief 创建一个新的日志
 * @return 创建的日志
 */
logger_t* logger_create();

/**
 * Flush 并关闭日志
 * @param logger 日志
 */
void logger_close(logger_t* logger);

/**
 * 设置日志输出级别
 * @param logger 日志
 * @param level 日志输出级别(越小级别越高，输出信息越少): 0~7 = {"EMERG ", "ALERT ", " CRIT ", "ERROR ", " WARN ", " NOTE ", " INFO ", "DEBUG "}
 */
void logger_set_level(logger_t* logger, int level);

/**
 * 删除日志中指定的 writer
 * @param logger 日志
 * @param writer 通过 logger_add_xxx() 添加各种 writer 时返回的 writer 指针
 */
void logger_del_writer(logger_t* logger, log_writer_t *writer);

/**
 * 添加已经定义好的日志 writer
 * @param logger 日志
 * @param writer 已经定义好的日志 writer
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_writer(logger_t* logger, log_writer_t *writer);

/**
 * 添加日志 writer：任意的日志输出函数
 * @param logger 日志
 * @param write_func 日志输出函数
 * @param cookie 用于调用 write_func() 时的私有参数
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_writefunc(logger_t* logger, log_writefunc_t writefunc, void* cookie);

/**
 * 添加日志输出目的地：把一个 spdlog 日志添加到输出目的地
 * @param logger 日志
 * @param spdlog 一个 spdlog 日志指针(spdlog::logger*)
 * @note 注意: 要使用此函数，必须编译和连接 log4spdlog.cpp !
 */
log_writer_t* logger_add_spdlog(logger_t* logger, void *spdlog);

/**
 * 添加日志输出目的地：任意的收发器
 * @param logger 日志
 * @param trx 收发器
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_transceiver(logger_t* logger, transceiver_t* trx);

/**
 * 添加日志输出目的地：任意的文件
 * @param logger 日志
 * @param fp 文件指针
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_file(logger_t* logger, FILE* fp);

/**
 * 添加日志输出目的地：stdout
 * @param logger 日志
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_stdout(logger_t* logger);

/**
 * 添加日志输出目的地：rolling_file
 * @param logger 日志
 * @param filepath 日志文件路径
 * @param max_filesize 日志文件最大尺寸
 * @param max_filecount 日志文件绕接数量（如果文件超过最大尺寸，则循环覆盖最老的那个）
 * @return writer 指针。为 NULL 时表示失败。
 */
log_writer_t* logger_add_rolling_file(logger_t* logger, const char* filepath, int max_filesize, int max_filecount);

/**
 * 添加日志输出目的地：Linux/Unix 下输出到 syslog，Windows 下输出到 Windows 事件日志
 * @param logger 日志
 * @param name 在日志中要显示的应用名称。在 Windows 就是日志列表中的“来源”字段。
 * @return writer 指针。为 NULL 时表示失败。
 * @note 注意: 在 Windows 平台使用时，需要提前注册好对应的“来源”！具体请参见 log4winevent.mk.bat
 */
log_writer_t* logger_add_syslog(logger_t* logger, const char *name);

#ifdef __cplusplus
}
#endif

#endif  // _LOG_H_
